package com.yhk.wuchat.common.user.service.impl;

import cn.hutool.core.thread.NamedThreadFactory;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.yhk.wuchat.common.common.domain.vo.resp.ApiResult;
import com.yhk.wuchat.common.common.util.JsonUtils;
import com.yhk.wuchat.common.user.dao.UserDao;
import com.yhk.wuchat.common.user.domain.entity.IpDetail;
import com.yhk.wuchat.common.user.domain.entity.IpInfo;
import com.yhk.wuchat.common.user.domain.entity.User;
import com.yhk.wuchat.common.user.service.IpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @version 1.0
 * @Author moresuo
 * @Date 2024/4/16 22:58
 * @注释 IP解析框架
 */
@Service
@Slf4j
public class IpServiceImpl implements IpService, DisposableBean {

    private static ExecutorService executor = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(500), new NamedThreadFactory("refresh-ipDetail", false));

    @Resource
    private UserDao userDao;

    /**
     * 异步获取IP详情
     * @param id
     */
    @Override
    public void refreshIpDetailAsync(Long id) {
        executor.execute(() -> {
            //前面已经将用户信息存入数据库，这里直接从数据库中取
            User user = userDao.getById(id);
            IpInfo ipInfo = user.getIpInfo();
            if(ipInfo==null){
                return;
            }
            //判断是否需要刷新ip详情
            String ip = ipInfo.needRefreshIp();
            if(StrUtil.isBlank(ip)){
                return;
            }
            //IP刷新
            IpDetail ipDetail = tryGetIpDetailOrNullThreeTimes(ip);
            if(ipDetail!=null){
                //更新ip详情
                ipInfo.refreshIpDetail(ipDetail);
                User update = new User();
                update.setId(id);
                update.setIpInfo(ipInfo);
                userDao.updateById(update);
            }
        });
    }

    /**
     * 三次尝试获取ip详情
     * @param ip
     * @return
     */
    private static IpDetail tryGetIpDetailOrNullThreeTimes(String ip) {
        for(int i=0;i<3;i++){
            IpDetail ipDetail = getIpDetailOrNull(ip);
            if(ipDetail!=null){
                return ipDetail;
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                log.error("睡眠被打断");
            }
        }
        return  null;
    }

    /**
     * 调用淘宝接口，获取ip详情
     * @param ip
     * @return
     */
    private static IpDetail getIpDetailOrNull(String ip) {
        try {
            String url="https://ip.taobao.com/outGetIpInfo?ip=" + ip + "&accessKey=alibaba-inc";
            //发送get 请求
            String data = HttpUtil.get(url);
            ApiResult<IpDetail> result = JsonUtils.toObj(data, new TypeReference<ApiResult<IpDetail>>() {});
            //获取IP详情
            IpDetail detail = result.getData();
            if(detail==null){
                log.error("获取ip详情失败，ip:{}",ip);
                return null;
            }
            return detail;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 优雅停机
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        executor.shutdown();
        if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {//最多等30秒，处理不完就拉倒
            if (log.isErrorEnabled()) {
                log.error("Timed out while waiting for executor [{}] to terminate", executor);
            }
        }
    }

    public static void main(String[] args) {
        Date begin = new Date();
        for(int i=0;i<100;i++){
            int finalI = i;
            executor.execute(()->{
                IpDetail ipDetail = tryGetIpDetailOrNullThreeTimes("113.90.36.126");
                if(ipDetail!=null){
                    Date date = new Date();
                    System.out.println(String.format("第%d次获取成功，耗时：%dms", finalI,date.getTime()-begin.getTime()));
                }
            });
        }
    }
}
